package br.ufpe.cin.wormbox.core;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;

import br.ufpe.cin.wormbox.core.FachadaUsuario.Status;
import br.ufpe.cin.wormbox.exceptions.RPFTSocketException;
import br.ufpe.cin.wormbox.protocolo.RPFTPacket;
import br.ufpe.cin.wormbox.protocolo.RPFTServer;
import br.ufpe.cin.wormbox.protocolo.RPFTSocket;

public class ThreadCliente extends Thread {

	private Socket TCPS;
	private RPFTServer RPFTS;
	public DadosUsuario dados;
	private BufferedReader messageReader;
	private ObjectInputStream objectReader;
	private ObjectOutputStream objectWriter;
	private DataOutputStream messageWriter;
	private FachadaServidor pai;
	public String IP;
	private boolean continua = true;
	public boolean conectado = false;

	public ThreadCliente(Socket s, FachadaServidor pai) throws IOException {
		this.pai = pai;
		this.IP = null;
		this.TCPS = s;
		this.RPFTS = new RPFTServer();
		this.messageWriter = new DataOutputStream(this.TCPS.getOutputStream());
		this.objectWriter = new ObjectOutputStream(this.TCPS.getOutputStream());
		this.messageReader = new BufferedReader
				(new InputStreamReader(this.TCPS.getInputStream()));
		this.objectReader = new ObjectInputStream(this.TCPS.getInputStream());

		//this.dados = (DadosUsuario) this.objectReader.readObject();
	}

	public void fechar() throws IOException {
		this.TCPS.close();
		this.messageReader.close();
		this.objectReader.close();
		this.messageWriter.close();
		this.objectWriter.close();
		continua = conectado = false;
	}

	public void run() {
		String mensagem;
		File f;

		try {
			mensagem = this.messageReader.readLine();
			System.out.println(mensagem);

			if(mensagem.equals("cadastro")) {
				String nome, login, senha;

				nome = this.messageReader.readLine();
				login = this.messageReader.readLine();
				senha = this.messageReader.readLine();

				if(pai.server.inserirUsuario(login, new DadosUsuario(nome, login, senha))) {
					messageWriter.writeBytes("ok\n");
				} else {
					messageWriter.writeBytes("erro\n");
				}
			} else {
				do {
					if(mensagem.equals("login")) {
						String login, senha;

						login = messageReader.readLine();
						senha = messageReader.readLine();

						this.dados = pai.server.conferirLogin(login, senha);
						if(this.dados != null) {
							this.IP = this.TCPS.getInetAddress().toString();
							messageWriter.writeBytes("ok\n");
							messageWriter.writeBytes(dados.getUltimaSincronizacao()+"\n");
							conectado = true;
						} else {
							messageWriter.writeBytes("erro\n");
							continua = conectado = false;
						}
					} else if(mensagem.equals("comp")) {
						System.out.println("comparacao solicitada");
						Arquivo arvore = pai.server.gerarArvoreArquivos(dados.getLogin());

						objectWriter.writeObject(arvore);
						
						messageWriter.writeBytes(dados.getUltimaSincronizacao()+"\n");
					} else if(mensagem.equals("sync")) {
						System.out.println("recebi sync");

						Arquivo arvore = pai.server.gerarArvoreArquivos(dados.getLogin());
						System.out.println("gerei minha arvore");

						objectWriter.writeObject(arvore);
						System.out.println("enviei minha arvore");
						
						messageWriter.writeBytes(dados.getUltimaSincronizacao()+"\n");

						Relatorio relat = (Relatorio) objectReader.readObject();
						System.out.println("recebi relatorio");

						ArrayList<Elemento> listaElementos = relat.getDeletar();
						for(Elemento e : listaElementos) {
							f = new File(e.getPath());

							ServicosGerenciamentoArquivo.deletar(f);
						}

						messageReader.readLine();
						messageWriter.writeBytes(listaElementos.size() + "\n");

						messageWriter.writeBytes("sp "+this.RPFTS.getPort()+'\n');

						RPFTSocket transferSocket = RPFTS.aceitarConexao();

						listaElementos = relat.getEnviar();
						for(Elemento e : listaElementos) {
							f = new File(e.getPath());
							messageWriter.writeBytes(f.length()+"\n");

							if(f.isFile()) {
								transferSocket.enviar(f, new Transferencia("", 0, Status.SINCRONIZANDO));
							}
							System.out.println("Terminou de enviar "+f.getName());
							messageReader.readLine();
							System.out.println("OK recebido");
							this.dados.setUltimasincronizacao(System.currentTimeMillis());
						}
						System.out.println("enviei todos");

						listaElementos = relat.getReceber();
						RPFTPacket packet;
						FileOutputStream fos = null;
						for(Elemento e : listaElementos) {
							f = new File(e.getPath());
							if (e.size > 0) {
								f.createNewFile();
								try {
									fos = new FileOutputStream(f);
								} catch (FileNotFoundException ex) {}

								do {
									packet = transferSocket.receber();

									if (!packet.isFileEnd()) {
										fos.write(packet.getDados());
									}
								} while (!packet.isFileEnd());
							} else {
								if(e.isArquivo()) {
									f.createNewFile();
								} else {
									f.mkdirs();
								}
							}
							this.messageWriter.writeBytes("ok\n");
						}
						System.out.println("recebi todos.");

						try {
							transferSocket.disconnect();
						} catch (RPFTSocketException e1) {
							throw new RuntimeException("Falha na desconexao RPFT");
						}
					}


					if(continua) {
						mensagem = messageReader.readLine();
						if(mensagem.equals("logoff")) {
							this.messageWriter.writeBytes("ok\n");
							this.fechar();
						}
					}
				} while(continua);
			}

			pai.removerThread(this);
		} catch (SocketTimeoutException e) {
			throw new RuntimeException("Conexao perdida");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {

		} catch (InterruptedException e) {

		}
	}
}
